public class VModestMap{
  ArrayList giList;
  HistoryData hd;
  float tickSpan = 13;
  final float startX = 50;
  final float startY = APP_HEIGHT - 50;
  final float lineSpan = APP_WIDTH - 100;
  final float startNotchY = startY;
  final float endNotchY   = APP_HEIGHT - 60;
    
  float dmx;
  final float dmy = startY;
  
  final float startHX = 25;
  final float startHY = APP_HEIGHT - 150;
  final float hrLineSpan = 24 * tickSpan;
  float hmx = startHX;
  float hmy;
  String dateMetaInfo;  
  Long totalHrs;
  int dayValue;
  boolean playAll;
  boolean playMode;

  public VModestMap(InteractiveMap map) {
    dmx = startX;
    hmy = startHY;
    
    playAll = false;
    playMode = false;
    
    giList = new ArrayList();
    hd = new HistoryData(); 
    //ptList = new ArrayList();
    setInitialDateMetaInfo();
    dateMetaInfo = new String("");
  }
  
  private void setInitialDateMetaInfo() {
    ArrayList first = hd.getHourData(0); 
    HistoryTuple ht = (HistoryTuple)first.get(0);
    Calendar c = ht.cal;
    
    dateMetaInfo = new String(c.get(Calendar.MONTH) + "/" + c.get(Calendar.DAY_OF_MONTH) + "/" + c.get(Calendar.YEAR));
  }  
  
  void addSite(String url) {
    giList.add(new GeoInfo(url));  
  }
  
  private boolean isInDayBarRegion() {
    if (mouseY <= startNotchY && mouseY >= (endNotchY - 10) 
     && mouseX >= startX && mouseX <= (startX + lineSpan)) {
      return true;
     }         
 
     return false;
  }

  private boolean isInHrBarRegion() {
    if (mouseY <= startHY && mouseY >= (startHY - hrLineSpan)  
     && mouseX >= startHX && mouseX <= (startX + 15)) {
      return true;
     }     
     
     return false;
  }
  
  private boolean isInDMRegion() {
    if (mouseX >= (dmx - 10) && mouseX <= (dmx + 10) && mouseY >= (dmy - 10) && mouseY <= (dmy + 10)) 
      return true;
      
    return false;
  }

  private boolean isInHMRegion() {
    if (mouseX >= (hmx - 10) && mouseX <= (hmx + 10) && mouseY >= (hmy - 10) && mouseY <= (hmy + 10)) 
      return true;
      
    return false;
  }
  
  private int getHours() {
    int cd = -1;
    if (dmx > startX) {
      int spanX = (int)((dmx - startX) / 1.0);
      cd = ((spanX / (int)tickSpan) + 1) * 24;
    }
    
    return cd;
  }
    
  private void drawDayMarker() {  
    fill(#ff0000);
    strokeWeight(0);
    triangle(dmx, dmy, dmx - 5, dmy + 7, dmx + 5, dmy + 7);
  }

  private void drawHMarker() {  
    fill(#00ffff);
    strokeWeight(0);
    triangle(hmx, hmy, hmx - 7, hmy - 5, hmx - 7, hmy + 5);
  }
  
  private void drawTimeLine() {
    stroke(128);
    final int numObservedDays = hd.getNoOfObservedDays();
    
    // Day Timeline
    strokeWeight(2);
    stroke(255);    
    line(startX, startY, startX + lineSpan, startY);

    for (int i = 0; i < numObservedDays; i++) {
      float x = startX + (i * tickSpan);
      float enY = endNotchY;
      if(i % 10 == 0)
        enY -= 10;
      line(x, startNotchY, x, enY); 
    }
    
    //drawDayMarker();

    // Hour Timeline
    int curHr = 0;
    line(startHX, startHY, startHX, startHY - (tickSpan * 24));
    for(int i = 0; i <= 24; i++) {
      float y = startHY - (tickSpan * 24) + (i * tickSpan);
      float enX = startHX + 5;
      
      if(i % 6 == 0) {
        enX += 10;
        fill(color(255,0,0));
        text((24 - i) + " ", enX + 5, y + 2);
      }
      line(startHX, y, enX, y);
    }
    
    //drawHMarker();
  
    fill(#ff0000);
    strokeWeight(0);
    triangle(dmx, dmy, dmx - 5, dmy + 7, dmx + 5, dmy + 7); // Day marker  
    triangle(hmx, hmy, hmx - 7, hmy - 5, hmx - 7, hmy + 5); // Hour marker
        
  }
  
  private void drawMetaInfo(GeoInfo gi, Point2f p) {
    final String url = gi.getURL();
      strokeWeight(1);
      fill(191, 127, 0);
      rect(mouseX + 30, mouseY + 15, url.length() * 10 + 15, 30);
      strokeWeight(2);
      fill(255);
      
      latLon  = "(" + gi.getLat() + ", " + gi.getLon() + ")";
      text(gi.getURL(), mouseX + 50, mouseY + 27);
      text(latLon, mouseX + 50, mouseY + 42);
  }
  
  private void showTimeInfo(long hr) {
    ArrayList al = hd.getHourData((int)hr);
    if (al != null) { 
      HistoryTuple ht = (HistoryTuple)al.get(0);
      dateMetaInfo = ht.cal.get(Calendar.MONTH) + "/" + ht.cal.get(Calendar.DAY_OF_MONTH) + "/" + ht.cal.get(Calendar.YEAR);
    }
    
    strokeWeight(1);
    stroke(255);    
    fill(255, 255, 255);    
    triangle (dmx, dmy - 15, dmx - 50, dmy - 24, dmx + 50, dmy - 24);
    rect(dmx - 50, dmy - 45, 100, 20);
    strokeWeight(3);
    fill(0);
    text(dateMetaInfo, dmx - 37, dmy - 30);
  }
  
  String dispURL;
  String latLon;
//  ArrayList ptList;
  
  void setup() {

  }
  
  private void showForHour(int hr) {
    ArrayList currentList = hd.getHourData(hr);
    
    if(currentList == null) {
      return;  
    }
    
    giList = null;
    giList = new ArrayList();
    ListIterator itr = currentList.listIterator();
  
    while(itr.hasNext()) {
      final HistoryTuple ht = (HistoryTuple) itr.next();
      Calendar c = ht.cal;
      dateMetaInfo = c.get(Calendar.MONTH) + "/" + c.get(Calendar.DAY_OF_MONTH) + "/" + c.get(Calendar.YEAR);
      giList.add(new GeoInfo(ht.url));           
    }  
  }
  
  private void doPlayAll() {
        if (hmy > (startHY - hrLineSpan)) {
          hmy -= tickSpan;
          return; 
        }
        
        hmy = startHY;
        dmx += tickSpan;
  }
  
  private boolean isOnPlayButton() {
    if (mouseX >= 10 && mouseX <= 60 && mouseY >= 100 && mouseY <= 115)
      return true;
    return false;  
  }
  
  private boolean isOnStopButton() {
    if (mouseX >= 10 && mouseX <= 60 && mouseY >= 120 && mouseY <= 135)
      return true;
    return false;  
  }  
  private void drawPlayButton() {
    stroke(0, 255, 127);
    fill(0, 127, 0);
    rect(10, 100, 65, 15);
    fill(255, 0, 0);
    rect(10, 120, 65, 15);    
    fill(0);
    text("Play All", 12, 112);
    text("Stop/Reset", 12, 132);    
  }
  
  private void updateTimeInfo() {
    if (dmx > startX) {
      dayValue = (int)(((dmx - startX) / (int)tickSpan) + 1);
      totalHrs = (long)dayValue * 24;
    }
    
    totalHrs = (long) getHours() + (long)((startHY - hmy) / tickSpan);
    //println(totalHrs);
    showTimeInfo(totalHrs);    
    if(totalHrs >= 0) {
      showForHour(totalHrs.intValue());
    }    
  }
  
  void draw() {
    drawTimeLine();
    drawPlayButton();
    
    ListIterator itr = giList.listIterator();
    
    while(itr.hasNext()) {
      final GeoInfo gi = (GeoInfo) itr.next(); 
      final Point2f p = map.locationPoint(new com.modestmaps.geo.Location(gi.getLat(), gi.getLon())) ;
      stroke(255, 0, 0);
      strokeWeight(10);
      point(p.x, p.y);

      if(mouseX >= (p.x - 5) && (mouseX <= p.x + 5) && (mouseY <= p.y + 5) && (mouseY >= p.y - 5))
        drawMetaInfo(gi, p);      
    }
    
    updateTimeInfo();
    
    if (playAll == true && dmx < (startX + lineSpan) && dmy >= startHY) {
      doPlayAll();  
      delay(100);
    }
    else if (playAll || playMode){
      playMode = false;
      dmx = startX; 
      hmy = startHY;
    }
  }
  
  void keyReleased() {

  }
  
  
  // see if we're over any buttons, otherwise tell the map to drag
  void mouseDragged() {
    boolean hand = false;    
      if (!hand) {
        if(isInDMRegion() == false && isInHMRegion() == false)
          map.mouseDragged(); 
      }
  }
  
  // zoom in or out:
  void mouseWheel(int delta) {
    if (delta > 0) {
      map.sc *= 1.05;
    }
    else if (delta < 0) {
      map.sc *= 1.0/1.05; 
    }
  }
  
  // see if we're over any buttons, and respond accordingly:
  void mouseClicked() {
    if (isOnPlayButton()) {
      playMode = true;
      playAll = true; 
    }
    if (isOnStopButton()) {
      playAll = false;
      playMode = true;  
    }
    
    if(isInDayBarRegion()) {
      if (mouseX > dmx) {
        dmx += tickSpan; 
      }
      else if (mouseX < dmx) {
        dmx -= tickSpan; 
      }
    }
    else if(isInHrBarRegion()) {
      if(mouseY < hmy) {
          hmy -= tickSpan;
      }
       else if(mouseY > hmy) {
           hmy += tickSpan;
       }
    }
  } 
  
  void mousePressed() {
    if(isInDayBarRegion()) {
      if (mouseX > dmx) {
        dmx += tickSpan; 
      }
      else if (mouseX < dmx) {
        dmx -= tickSpan; 
      }
    }
    else if(isInHrBarRegion()) {
      if(mouseY < hmy) {
          hmy -= tickSpan;
      }
       else if(mouseY > hmy) {
           hmy += tickSpan;
       }
    }    
  }
}
